/*
 * $Id: DateSelectionModel.java 3100 2008-10-14 22:33:10Z rah003 $
 *
 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jdesktop.swingx.calendar;

import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.SortedSet;
import java.util.TimeZone;

import org.jdesktop.swingx.event.DateSelectionListener;

/**
 * The Model used by calendar components. It controls the Calendar to use and
 * keeps selection-related state.
 * 
 * @author Joshua Outwater
 */
public interface DateSelectionModel {
	public static enum SelectionMode {
		/**
		 * Mode that allows for selection of a single day.
		 */
		SINGLE_SELECTION,
		/**
		 * Mode that allows for selecting of multiple consecutive days.
		 */
		SINGLE_INTERVAL_SELECTION,
		/**
		 * Mode that allows for selecting disjoint days.
		 */
		MULTIPLE_INTERVAL_SELECTION
	}

	// ---------------------- mode
	/**
	 * Get the selection mode.
	 *
	 * @return return the current selection mode
	 */
	public SelectionMode getSelectionMode();

	/**
	 * Set the selection mode.
	 *
	 * @param mode
	 *            new selection mode
	 */
	public void setSelectionMode(final SelectionMode mode);

	// -------------------- calendar
	/**
	 * Returns a clone of the calendar used by this model. It's date is
	 * unspecified.
	 * 
	 * @return a clone of the calendar used by this model.
	 */
	public Calendar getCalendar();

	/**
	 * Gets what the first day of the week is; e.g.,
	 * <code>Calendar.SUNDAY</code> in the U.S., <code>Calendar.MONDAY</code> in
	 * France. This is needed when the model selection mode is
	 * <code>WEEK_INTERVAL_SELECTION</code>.
	 * 
	 * PENDING JW: move week-interval selection from JXMonthView into the model.
	 *
	 * @return int The first day of the week.
	 * @see #setFirstDayOfWeek(int)
	 */
	public int getFirstDayOfWeek();

	/**
	 * Sets what the first day of the week is. E.g.,
	 * <code>Calendar.SUNDAY</code> in US, <code>Calendar.MONDAY</code> in
	 * France. Fires a DateSelectionEvent of type CALENDAR_CHANGED, if the value
	 * is different from the old.
	 * <p>
	 * 
	 * The default value depends on the Calendar's default.
	 * 
	 * PENDING JW: actually, it's a bound property. Use a
	 * propertyChangeListener?
	 *
	 * @param firstDayOfWeek
	 *            The first day of the week.
	 * @see #getFirstDayOfWeek()
	 * @see java.util.Calendar
	 */
	public void setFirstDayOfWeek(final int firstDayOfWeek);

	/**
	 * Gets the minimal number of days in the first week of the year.
	 *
	 * @return int the minimal number of days in the first week of the year.
	 */
	public int getMinimalDaysInFirstWeek();

	/**
	 * Sets the minimal number of days in the first week of the year. Fires a
	 * DateSelectionEvent of type CALENDAR_CHANGED, if the value is different
	 * from the old.
	 * 
	 * The default value depends on the Calendar's default.
	 * 
	 * PENDING JW: actually, it's a bound property. Use a
	 * propertyChangeListener?
	 *
	 * @param minimalDays
	 *            the minimal number of days in the first week of the year.
	 * @see #getMinimalDaysInFirstWeek()
	 * @see java.util.Calendar
	 */
	public void setMinimalDaysInFirstWeek(final int minimalDays);

	/**
	 * Returns the TimeZone of this model.
	 * 
	 * @return the TimeZone of this model.
	 * @see #setTimeZone(TimeZone)
	 */
	public TimeZone getTimeZone();

	/**
	 * Sets the TimeZone of this model. Fires a DateSelectionEvent of type
	 * CALENDAR_CHANGED if the new value is different from the old.
	 * 
	 * The default value depends on the Calendar's default.
	 * 
	 * PENDING JW: actually, it's a bound property. Use a
	 * propertyChangeListener?
	 * 
	 * @param timeZone
	 *            the TimeZone to use in this model, must not be null.
	 * @see #getTimeZone()
	 */
	public void setTimeZone(TimeZone timeZone);

	/**
	 * Returns the Locale of this model's calendar.
	 * 
	 * @return the Locale of this model's calendar.
	 */
	public Locale getLocale();

	/**
	 * Sets the Locale of this model's calendar. Fires a DateSelectionEvent of
	 * type CALENDAR_CHANGED if the new value is different from the old.
	 * <p>
	 * 
	 * The default value is Locale.default().
	 * <p>
	 * 
	 * PENDING JW: fall back to JComponent.getDefaultLocale instead? We use this
	 * with components anyway?
	 * <p>
	 * PENDING JW: actually, it's a bound property. Use a
	 * propertyChangeListener?
	 * 
	 * @param locale
	 *            the Locale to use. If null, the default Locale is used.
	 */
	public void setLocale(Locale locale);

	// -------------------- selection

	/**
	 * Adds the specified selection interval to the selection model.
	 *
	 * @param startDate
	 *            interval start date, must not be null
	 * @param endDate
	 *            interval end date >= start date, must not be null
	 * @throws NullPointerException
	 *             if any of the dates is null
	 */
	public void addSelectionInterval(Date startDate, Date endDate);

	/**
	 * Sest the specified selection interval to the selection model.
	 *
	 * @param startDate
	 *            interval start date, must not be null
	 * @param endDate
	 *            interval end date >= start date, must not be null
	 * @throws NullPointerException
	 *             if any of the dates is null
	 */
	public void setSelectionInterval(Date startDate, Date endDate);

	/**
	 * Removes the specifed selection interval from the selection model. If the
	 * selection is changed by this method, it fires a DateSelectionEvent of
	 * type DATES_REMOVED.
	 *
	 * @param startDate
	 *            interval start date, must not be null
	 * @param endDate
	 *            interval end date >= start date, must not be null
	 * @throws NullPointerException
	 *             if any of the dates is null
	 */
	public void removeSelectionInterval(Date startDate, Date endDate);

	/**
	 * Clears any selection from the selection model. Fires an Event of type
	 * SELECTION_CLEARED if there had been a selection, does nothing otherwise.
	 */
	public void clearSelection();

	/**
	 * Returns the current selection.
	 *
	 * @return sorted set of selected dates, guaranteed to be never null.
	 */
	public SortedSet<Date> getSelection();

	/**
	 * Returns the earliest date in the selection or null if the selection is
	 * empty.
	 * 
	 * @return the earliest date in the selection, or null if isSelectionEmpty.
	 * 
	 * @see #getLastSelectionDate()
	 * @see #getSelection()
	 * @see #isSelectionEmpty()
	 */
	public Date getFirstSelectionDate();

	/**
	 * Returns the latest date in the selection or null if the selection is
	 * empty.
	 * 
	 * @return the lastest date in the selection, or null if isSelectionEmpty.
	 * 
	 * @see #getFirstSelectionDate()
	 * @see #getSelection()
	 * @see #isSelectionEmpty()
	 */
	public Date getLastSelectionDate();

	/**
	 * Returns true if the date specified is selected, false otherwise.
	 * <p>
	 * 
	 * Note: it is up to implementations to define the exact notion of selected.
	 * It does not imply the exact date as given is contained the set returned
	 * from getSelection().
	 * 
	 * @param date
	 *            date to check for selection, must not be null
	 * @return true if the date is selected, false otherwise
	 * @throws NullPointerException
	 *             if the date is null
	 */
	public boolean isSelected(final Date date);

	/**
	 * Returns a normalized Date as used by the implementation, if any. F.i.
	 * DaySelectionModel returns the start of the day in the model's calendar.
	 * If no normalization is applied, a clone of the Date itself is returned.
	 * The given Date is never changed.
	 * <p>
	 * 
	 * The overall contract:
	 * 
	 * <pre>
	 * <code>
	 * if ((date != null) &amp;&amp; isSelectable(date)) {
	 *     setSelectionInterval(date, date);
	 *     assertEquals(getNormalized(date), getFirstSelectionDate();
	 * }
	 * </code>
	 * </pre>
	 * 
	 * 
	 * @return the date as it would be normalized before used in the model, must
	 *         not be null.
	 * @throws NullPointerException
	 *             if given date is null.
	 */
	public Date getNormalizedDate(Date date);

	/**
	 * Returns true if the selection is empty, false otherwise.
	 *
	 * @return true if the selection is empty, false otherwise
	 */
	public boolean isSelectionEmpty();

	/**
	 * Returns a <code>SortedSet</code> of
	 * <code>Date</codes>s that are unselectable.
	 *
	 * @return sorted set of dates
	 */
	public SortedSet<Date> getUnselectableDates();

	/**
	 * Sets a collection of dates which are not selectable.
	 * <p>
	 * 
	 * Note: it is up to implementations to define the exact notion of
	 * unselectableDate. It does not imply the only the exact date as given is
	 * unselectable, it might have a period like "all dates on the same day".
	 * 
	 * PENDING JW: any collection would do - why insist on a SortedSet?
	 *
	 * @param unselectableDates
	 *            dates that are unselectable, must not be null and must not
	 *            contain null dates.
	 */
	public void setUnselectableDates(SortedSet<Date> unselectableDates);

	/**
	 * Returns true is the specified date is unselectable.
	 *
	 * @param unselectableDate
	 *            the date to check for unselectability, must not be null.
	 * @return true is the date is unselectable, false otherwise
	 */
	public boolean isUnselectableDate(Date unselectableDate);

	/**
	 * Return the upper bound date that is allowed to be selected for this
	 * model.
	 *
	 * @return upper bound date or null if not set
	 */
	public Date getUpperBound();

	/**
	 * Set the upper bound date that is allowed to be selected for this model.
	 *
	 * @param upperBound
	 *            upper bound
	 */
	public void setUpperBound(final Date upperBound);

	/**
	 * Return the lower bound date that is allowed to be selected for this
	 * model.
	 *
	 * @return lower bound date or null if not set
	 */
	public Date getLowerBound();

	/**
	 * Set the lower bound date that is allowed to be selected for this model.
	 *
	 * @param lowerBound
	 *            lower bound date or null if not set
	 */
	public void setLowerBound(final Date lowerBound);

	/**
	 * Set the property to mark upcoming selections as intermediate/ final. This
	 * will fire a event of type adjusting_start/stop.
	 * 
	 * The default value is false.
	 * 
	 * Note: Client code marking as intermediate must take care of finalizing
	 * again.
	 * 
	 * @param adjusting
	 *            a flag to turn the adjusting property on/off.
	 */
	public void setAdjusting(boolean adjusting);

	/**
	 * Returns the property to decide whether the selection is intermediate or
	 * final.
	 * 
	 * @return the adjusting property.
	 */
	public boolean isAdjusting();

	/**
	 * Add the specified listener to this model.
	 *
	 * @param listener
	 *            listener to add to this model
	 */
	public void addDateSelectionListener(DateSelectionListener listener);

	/**
	 * Remove the specified listener to this model.
	 *
	 * @param listener
	 *            listener to remove from this model
	 */
	public void removeDateSelectionListener(DateSelectionListener listener);

}
